﻿// -------------------------------------------------------------------------------------------------------------------------------
// <copyright file="TestConstructor.cs" company="Exul">
//     Copyright (c) Exul. All rights reserved.
// </copyright>
// <author>
//     Exul
// </author>
// -------------------------------------------------------------------------------------------------------------------------------

namespace ExulLibrary.Mathematics.LinearAlgebra.Tests.CrcsSparseMatrixTest
{
    #region Usings

    using System;

    using ExulLibrary.Exceptions;

    using NUnit.Framework;

    #endregion

    /// <summary>
    ///     Test suite for the constructor of <see cref="CrcsSparseMatrix{T}"/>.
    /// </summary>
    [TestFixture]
    [Category("ExulLibrary.Mathematics.LinearAlgebra.CrcsSparseMatrix{T}")]
    public sealed class TestConstructor
    {
        /// <summary>
        ///     Tests the constructor when all parameters have valid values and specifies square matrix
        ///     with data parameters equal to <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenParametersAreValidForSquareMatrixAndDataIsNull()
        {
            // Arrange
            // Matrix:
            // x x
            // x x
            var rowsBegins = new[] { 0, 0, 1 };
            var columns = new[] { 0 };
            var columnsBegins = new[] { 0, 0, 1 };
            var rows = new[] { 0 };

            // Act
            var matrix = new CrcsSparseMatrix<object>(rowsBegins, columns, columnsBegins, rows);

            // Assert
            Assert.That(matrix.RowsCount, Is.EqualTo(2));
            Assert.That(matrix.ColumnsCount, Is.EqualTo(2));
            Assert.That(matrix.IsSquare, Is.True);
            Assert.That(matrix.LowTriangleRowsBegins, Is.SameAs(rowsBegins));
            Assert.That(matrix.LowTriangleColumnsIndexes, Is.SameAs(columns));
            Assert.That(matrix.HighTriangleColumnsBegins, Is.SameAs(columnsBegins));
            Assert.That(matrix.HighTriangleRowsIndexes, Is.SameAs(rows));
            Assert.That(matrix.DiagonalValues, Is.Not.Null & Has.Length.EqualTo(2));
            Assert.That(matrix.LowTriangleValues, Is.Not.Null & Has.Length.EqualTo(1));
            Assert.That(matrix.HighTriangleValues, Is.Not.Null & Has.Length.EqualTo(1));
        }

        /// <summary>
        ///     Tests the constructor when all parameters have valid values and specifies square matrix
        ///     with data parameter not equal to <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenParametersAreValidForSquareMatrixWhenDataIsNotNull()
        {
            // Arrange
            // Matrix:
            // x x
            // x x
            var rowsBegins = new[] { 0, 0, 1 };
            var columns = new[] { 0 };
            var columnsBegins = new[] { 0, 0, 1 };
            var rows = new[] { 0 };
            var diagonal = new object[2];
            var lowValues = new object[1];
            var highValues = new object[1];

            // Act
            var matrix = new CrcsSparseMatrix<object>(rowsBegins, columns, columnsBegins, rows, lowValues, diagonal, highValues);

            // Assert
            Assert.That(matrix.RowsCount, Is.EqualTo(2));
            Assert.That(matrix.ColumnsCount, Is.EqualTo(2));
            Assert.That(matrix.IsSquare, Is.True);
            Assert.That(matrix.LowTriangleRowsBegins, Is.SameAs(rowsBegins));
            Assert.That(matrix.LowTriangleColumnsIndexes, Is.SameAs(columns));
            Assert.That(matrix.HighTriangleColumnsBegins, Is.SameAs(columnsBegins));
            Assert.That(matrix.HighTriangleRowsIndexes, Is.SameAs(rows));
            Assert.That(matrix.DiagonalValues, Is.SameAs(diagonal));
            Assert.That(matrix.LowTriangleValues, Is.SameAs(lowValues));
            Assert.That(matrix.HighTriangleValues, Is.SameAs(highValues));
        }

        /// <summary>
        ///     Tests the constructor when all parameters have valid values and specifies non-square matrix
        ///     with data parameters equal to <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenParametersAreValidForNonSquareMatrixWhenDataIsNull()
        {
            // Arrange
            // Matrix:
            // x x 0
            // x x x
            var rowsBegins = new[] { 0, 0, 1 };
            var columns = new[] { 0 };
            var columnsBegins = new[] { 0, 0, 1, 2 };
            var rows = new[] { 0, 1 };

            // Act
            var matrix = new CrcsSparseMatrix<object>(rowsBegins, columns, columnsBegins, rows);

            // Assert
            Assert.That(matrix.RowsCount, Is.EqualTo(2));
            Assert.That(matrix.ColumnsCount, Is.EqualTo(3));
            Assert.That(matrix.IsSquare, Is.False);
            Assert.That(matrix.LowTriangleRowsBegins, Is.SameAs(rowsBegins));
            Assert.That(matrix.LowTriangleColumnsIndexes, Is.SameAs(columns));
            Assert.That(matrix.HighTriangleColumnsBegins, Is.SameAs(columnsBegins));
            Assert.That(matrix.HighTriangleRowsIndexes, Is.SameAs(rows));
            Assert.That(matrix.DiagonalValues, Is.Not.Null & Has.Length.EqualTo(2));
            Assert.That(matrix.LowTriangleValues, Is.Not.Null & Has.Length.EqualTo(1));
            Assert.That(matrix.HighTriangleValues, Is.Not.Null & Has.Length.EqualTo(2));
        }

        /// <summary>
        ///     Tests the constructor when all parameters have valid values and specifies non-square matrix
        ///     with data parameters not equal to <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenParametersAreValidForNonSquareMatrixWhenDataIsNotNull()
        {
            // Arrange
            // Matrix:
            // x x 0
            // x x x
            var rowsBegins = new[] { 0, 0, 1 };
            var columns = new[] { 0 };
            var columnsBegins = new[] { 0, 0, 1, 2 };
            var rows = new[] { 0, 1 };
            var diagonal = new object[2];
            var lowValues = new object[1];
            var highValues = new object[2];

            // Act
            var matrix = new CrcsSparseMatrix<object>(rowsBegins, columns, columnsBegins, rows, lowValues, diagonal, highValues);

            // Assert
            Assert.That(matrix.RowsCount, Is.EqualTo(2));
            Assert.That(matrix.ColumnsCount, Is.EqualTo(3));
            Assert.That(matrix.IsSquare, Is.False);
            Assert.That(matrix.LowTriangleRowsBegins, Is.SameAs(rowsBegins));
            Assert.That(matrix.LowTriangleColumnsIndexes, Is.SameAs(columns));
            Assert.That(matrix.HighTriangleColumnsBegins, Is.SameAs(columnsBegins));
            Assert.That(matrix.HighTriangleRowsIndexes, Is.SameAs(rows));
            Assert.That(matrix.DiagonalValues, Is.SameAs(diagonal));
            Assert.That(matrix.LowTriangleValues, Is.SameAs(lowValues));
            Assert.That(matrix.HighTriangleValues, Is.SameAs(highValues));
        }

        #if SAFE

        /// <summary>
        ///     Tests the constructor when lowTriangleRowsBegins parameter is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenRowsBeginsParameterIsNull()
        {
            // Arrange
            var lowTriangleColumnsIndexes = new[] { 0 };
            var highTriangleColumnsBegins = new[] { 0, 0, 1, 2 };
            var highTriangleRowsIndexes = new[] { 0, 1 };

            // Act and Assert
            Assert.That(
                () => new CrcsSparseMatrix<object>(
                    null, lowTriangleColumnsIndexes, highTriangleColumnsBegins, highTriangleRowsIndexes),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("lowTriangleRowsBegins"));
        }

        /// <summary>
        ///     Tests the constructor when lowTriangleRowsBegins parameter has the number of elements less than 2.
        /// </summary>
        [Test]
        public void WhenRowsBeginsParameterHasNotEnoughElements()
        {
            // Arrange
            var lowTriangleRowsBegins = new[] { 0 };

            // length less than 2
            var lowTriangleColumnsIndexes = new[] { 0 };
            var highTriangleColumnsBegins = new[] { 0, 0, 1, 2 };
            var highTriangleRowsIndexes = new[] { 0, 1 };

            // Act and Assert
            Assert.That(
                () => new CrcsSparseMatrix<object>(
                    lowTriangleRowsBegins, lowTriangleColumnsIndexes, highTriangleColumnsBegins, highTriangleRowsIndexes),
                Throws.TypeOf<NoEnoughElementsException>().With.Property("ParamName").EqualTo("lowTriangleRowsBegins"));
        }

        /// <summary>
        ///     Tests the constructor when lowTriangleColumnsIndexes parameter is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenColumnsIndexesParameterIsNull()
        {
            // Arrange
            var lowTriangleRowsBegins = new[] { 0, 0, 1 };
            var highTriangleColumnsBegins = new[] { 0, 0, 1, 2 };
            var highTriangleRowsIndexes = new[] { 0, 1 };

            // Act and Assert
            Assert.That(
                () => new CrcsSparseMatrix<object>(
                    lowTriangleRowsBegins, null, highTriangleColumnsBegins, highTriangleRowsIndexes),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("lowTriangleColumnsIndexes"));
        }

        /// <summary>
        ///     Tests the constructor when lowTriangleColumnsIndexes parameter has the number of elements less than
        ///     rowsBegins[rowsCount] - rowsBegins[0].
        /// </summary>
        [Test]
        public void WhenColumnsIndexesParameterHasNotEnoughElements()
        {
            // Arrange
            var lowTriangleRowsBegins = new[] { 0, 0, 1 };
            var lowTriangleColumnsIndexes = new int[0];

            // length less than 1 - 0
            var highTriangleColumnsBegins = new[] { 0, 0, 1, 2 };
            var highTriangleRowsIndexes = new[] { 0, 1 };

            // Act and Assert
            Assert.That(
                () => new CrcsSparseMatrix<object>(
                    lowTriangleRowsBegins, lowTriangleColumnsIndexes, highTriangleColumnsBegins, highTriangleRowsIndexes),
                Throws.TypeOf<NoEnoughElementsException>().With.Property("ParamName").EqualTo("lowTriangleColumnsIndexes"));
        }

        /// <summary>
        ///     Tests the constructor when highTriangleColumnsBegins parameter is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenColumnsBeginsParameterIsNull()
        {
            // Arrange
            var lowTriangleRowsBegins = new[] { 0, 0, 1 };
            var lowTriangleColumnsIndexes = new[] { 0 };
            var highTriangleRowsIndexes = new[] { 0, 1 };

            // Act and Assert
            Assert.That(
                () => new CrcsSparseMatrix<object>(
                    lowTriangleRowsBegins, lowTriangleColumnsIndexes, null, highTriangleRowsIndexes),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("highTriangleColumnsBegins"));
        }

        /// <summary>
        ///     Tests the constructor when highTriangleColumnsBegins parameter has the number of element less than 2.
        /// </summary>
        [Test]
        public void WhenColumnsBeginsParameterHasNotEnoughElements()
        {
            // Arrange
            var lowTriangleRowsBegins = new[] { 0, 0, 1 };
            var lowTriangleColumnsIndexes = new[] { 0 };
            var highTriangleColumnsBegins = new[] { 0 };

            // length less than 2
            var highTriangleRowsIndexes = new[] { 0, 1 };

            // Act and Assert
            Assert.That(
                () => new CrcsSparseMatrix<object>(
                    lowTriangleRowsBegins, lowTriangleColumnsIndexes, highTriangleColumnsBegins, highTriangleRowsIndexes),
                Throws.TypeOf<NoEnoughElementsException>().With.Property("ParamName").EqualTo("highTriangleColumnsBegins"));
        }

        /// <summary>
        ///     Tests the constructor when highTriangleRowsIndexes parameter is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenRowsIndexesParameterIsNull()
        {
            // Arrange
            var lowTriangleRowsBegins = new[] { 0, 0, 1 };
            var lowTriangleColumnsIndexes = new[] { 0 };
            var highTriangleColumnsBegins = new[] { 0, 0, 1, 2 };

            // Act and Assert
            Assert.That(
                () => new CrcsSparseMatrix<object>(
                    lowTriangleRowsBegins, lowTriangleColumnsIndexes, highTriangleColumnsBegins, null),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("highTriangleRowsIndexes"));
        }

        /// <summary>
        ///     Tests the constructor when highTriangleRowsIndexes parameter has the number of elements less than
        ///     columnsBegins[columnsCount] - columnsBegins[0].
        /// </summary>
        [Test]
        public void WhenRowsIndexesParameterHasNotEnoughElements()
        {
            // Arrange
            var lowTriangleRowsBegins = new[] { 0, 0, 1 };
            var lowTriangleColumnsIndexes = new[] { 0 };
            var highTriangleColumnsBegins = new[] { 0, 0, 1, 2 };

            // length less than 2 - 0
            var highTriangleRowsIndexes = new[] { 0 };

            // Act and Assert
            Assert.That(
                () => new CrcsSparseMatrix<object>(
                    lowTriangleRowsBegins, lowTriangleColumnsIndexes, highTriangleColumnsBegins, highTriangleRowsIndexes),
                Throws.TypeOf<NoEnoughElementsException>().With.Property("ParamName").EqualTo("highTriangleRowsIndexes"));
        }

        /// <summary>
        ///     Tests the constructor when diagonalData parameter is not <see langword="null"/> and has the number of elements
        ///     less than min(rowsCount, columnsCount).
        /// </summary>
        [Test]
        public void WhenDiagonalDataParameterIsNotNullAndHasNoEnoughElements()
        {
            // Arrange
            var lowTriangleRowsBegins = new[] { 0, 0, 1 };
            var lowTriangleColumnsIndexes = new[] { 0 };
            var highTriangleColumnsBegins = new[] { 0, 0, 1, 2 };
            var highTriangleRowsIndexes = new[] { 0, 1 };

            // length less than min(2, 3)
            var diagonalData = new object[1];

            // Act and Assert
            Assert.That(
                () => new CrcsSparseMatrix<object>(
                    lowTriangleRowsBegins,
                    lowTriangleColumnsIndexes,
                    highTriangleColumnsBegins,
                    highTriangleRowsIndexes,
                    null,
                    diagonalData,
                    null),
                Throws.TypeOf<NoEnoughElementsException>().With.Property("ParamName").EqualTo("diagonalData"));
        }

        /// <summary>
        ///     Tests the constructor when lowTriangleData parameter is not <see langword="null"/> and has the number of elements
        ///     less than rowsBegins[rowsCount] - rowsBegins[0].
        /// </summary>
        [Test]
        public void WhenLowTriangleDataParameterIsNotNullAndHasNotEnoughElements()
        {
            // Arrange
            var lowTriangleRowsBegins = new[] { 0, 0, 1 };
            var lowTriangleColumnsIndexes = new[] { 0 };
            var highTriangleColumnsBegins = new[] { 0, 0, 1, 2 };
            var highTriangleRowsIndexes = new[] { 0, 1 };

            // length less than 1 - 0
            var lowTriangleData = new object[0];

            // Act and Assert
            Assert.That(
                () => new CrcsSparseMatrix<object>(
                          lowTriangleRowsBegins,
                          lowTriangleColumnsIndexes,
                          highTriangleColumnsBegins,
                          highTriangleRowsIndexes,
                          lowTriangleData,
                          null,
                          null),
                Throws.TypeOf<NoEnoughElementsException>().With.Property("ParamName").EqualTo("lowTriangleData"));
        }

        /// <summary>
        ///     Tests the constructor when highTriangleData parameter is not <see langword="null"/> and has the number of elements
        ///     less than columnsBegins[columnsCount] - columnsBegins[0].
        /// </summary>
        [Test]
        public void WhenHighTriangleDataParameterIsNotNullAndHasNotEnoughElements()
        {
            // Arrange
            var lowTriangleRowsBegins = new[] { 0, 0, 1 };
            var lowTriangleColumnsIndexes = new[] { 0 };
            var highTriangleColumnsBegins = new[] { 0, 0, 1, 2 };
            var highTriangleRowsIndexes = new[] { 0, 1 };

            // length less than 2 - 0
            var highTriangleData = new object[1];

            // Act and Assert
            Assert.That(
                () => new CrcsSparseMatrix<object>(
                          lowTriangleRowsBegins,
                          lowTriangleColumnsIndexes,
                          highTriangleColumnsBegins,
                          highTriangleRowsIndexes,
                          null,
                          null,
                          highTriangleData),
                Throws.TypeOf<NoEnoughElementsException>().With.Property("ParamName").EqualTo("highTriangleData"));
        }

        #endif
    }
}